/*
 * Copyright 2009 SHOP.COM
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.shop.opensocial.mediated.config;

import com.shop.opensocial.mediated.driver.OpenSocialServiceMediatorDriver;
import com.shop.opensocial.mediated.util.OAuthValidator;
import com.shop.opensocial.mediated.services.ContainerScriptsAndConstantsMediator;
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Method for exchanging values between web applications
 *
 * @author Jordan Zimmerman
 */
public class MediatedDriverFactory
{
	/**
	 * Must be called early in your application to register the driver instance
	 *
	 * @param driver the driver
	 */
	public static void 	setDriver(OpenSocialServiceMediatorDriver driver)
	{
		getActualReference().driver.set(driver);
	}

	/**
	 * Returns the OAuth validator instance
	 *
	 * @return validator instance
	 */
	public static OAuthValidator getOAuthValidator()
	{
		OAuthValidator 		validator = fRef.validator.get();
		if ( validator == null )
		{
			validator = getActualReference().validator.get();
			if ( validator == null )
			{
				throw new IllegalStateException("Mediated WAR has not initialized correctly.");
			}
			fRef.validator.set(validator);
		}
		return validator;
	}

	/**
	 * Returns the Container Scripts Mediator instance
	 *
	 * @return container scripts instance
	 */
	public static ContainerScriptsAndConstantsMediator getContainerScriptsAndConstantsMediator()
	{
		ContainerScriptsAndConstantsMediator scripts = fRef.scripts.get();
		if ( scripts == null )
		{
			scripts = getActualReference().scripts.get();
			if ( scripts == null )
			{
				throw new IllegalStateException("Mediated WAR has not initialized correctly.");
			}
			fRef.scripts.set(scripts);
		}
		return scripts;
	}

	/**
	 * Used internally to access the registered driver
	 *
	 * @return currently set driver
	 * @deprecated INTERNAL USE ONLY
	 */
	public static OpenSocialServiceMediatorDriver getDriver()
	{
		OpenSocialServiceMediatorDriver driver = fRef.driver.get();
		if ( driver == null )
		{
			driver = getActualReference().driver.get();
			if ( driver == null )
			{
				throw new IllegalStateException("MediatedDriverFactory.set() has not been called");
			}
			fRef.driver.set(driver);
		}
		return driver;
	}

	/**
	 * Used internally only
	 *
	 * @param validator the validator to set
	 * @deprecated INTERNAL USE ONLY
	 */
	public static void		setValidator(OAuthValidator validator)
	{
		getActualReference().validator.set(validator);
	}

	/**
	 * Used internally only
	 *
	 * @param scripts scripts to set
	 * @deprecated INTERNAL USE ONLY
	 */
	public static void setScripts(ContainerScriptsAndConstantsMediator scripts)
	{
		getActualReference().scripts.set(scripts);
	}

	private static class RefsRecord
	{
		final AtomicReference<OpenSocialServiceMediatorDriver>		driver = new AtomicReference<OpenSocialServiceMediatorDriver>();
		final AtomicReference<OAuthValidator>						validator = new AtomicReference<OAuthValidator>();
		final AtomicReference<ContainerScriptsAndConstantsMediator> 			scripts = new AtomicReference<ContainerScriptsAndConstantsMediator>();
	}

	private static RefsRecord		getActualReference()
	{
		/*
			Each app in a J2EE container is usually loaded via an independent class loader. This prevents
			them from setting globals that each can see. Here, the System class loader is used as an
			interchange medium. A version of this class is instantiated using the System class loader.
			All apps in a container will see the same values.
		 */

		try
		{
			Class<?> 		theClass = Class.forName(MediatedDriverFactory.class.getName(), true, ClassLoader.getSystemClassLoader());
			Field 			theField = theClass.getDeclaredField("fRef");
			theField.setAccessible(true);
			return (RefsRecord)theField.get(null);
		}
		catch ( Exception e )
		{
			throw new IllegalStateException(e);
		}
	}

	private MediatedDriverFactory()
	{
	}

	private static final RefsRecord  	fRef = new RefsRecord();
}
